/*
Copyright 2005  Vitaliy Shevchuk (shevit@users.sourceforge.net)

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

*/

package org.ajaxanywhere;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import org.ajaxanywhere.parser.StringMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Date: 23 juil. 2005 Time: 21:19:14
 */
public class BufferResponseWrapper extends HttpServletResponseWrapper implements AAConstants {
	private final static Logger LOGGER = LoggerFactory.getLogger(BufferResponseWrapper.class);
	private final static Pattern startZonePattern = Pattern.compile(ZONE_START_REGEX, Pattern.CASE_INSENSITIVE);
	PrintWriter pw;
	ServletOutputStream sos;
	private StringWriter writerBuffer;
	private ByteArrayOutputStream streamBuffer;

	HttpServletResponse originalResponse;

	private String redirect;

	private Map<String, String> zoneHtmlMap;

	public static void main(String[] args) {
		Matcher m = startZonePattern.matcher("<div style=\"display:inline;\" id=\"aazone.reportZone\">");
		while (m.find()) {
			System.out.println(m.group());
		}
	}

	public BufferResponseWrapper(HttpServletResponse httpServletResponse) {
		super(httpServletResponse);
		originalResponse = httpServletResponse;
	}

	public PrintWriter getWriter() throws IOException {
		if (writerBuffer == null) {
			writerBuffer = new StringWriter();
			pw = new PrintWriter(writerBuffer);
		}
		return pw;
	}

	public ServletOutputStream getOutputStream() throws IOException {
		if (streamBuffer == null) {
			streamBuffer = new ByteArrayOutputStream();
			sos = new ServletOutputStream() {
				public void write(int b) throws IOException {
					streamBuffer.write(b);
				}

				public void write(byte b[]) throws IOException {
					streamBuffer.write(b);
				}

				public void write(byte b[], int off, int len) throws IOException {
					streamBuffer.write(b, off, len);
				}
			};
		}
		return sos;
	}

	/**
	 * Outputs the content to OutputStream if the application is using it or to the
	 * Writer otherwise.
	 */
	public void output(String content) throws IOException {
		if (streamBuffer != null) {
			streamBuffer.write(content.getBytes(originalResponse.getCharacterEncoding()));
		} else {
			writerBuffer.write(content);
		}
	}

	public String getBuffer() {
		if (streamBuffer != null) {
			try {
				return streamBuffer.toString(originalResponse.getCharacterEncoding());
			} catch (UnsupportedEncodingException e) {
				return streamBuffer.toString();
			}
		} else if (writerBuffer != null)
			return writerBuffer.toString();
		else
			return "";
	}

	public HttpServletResponse getOriginalResponse() {
		return originalResponse;
	}

	public String getRedirect() {
		return redirect;
	}

	public void sendRedirect(String redirect) throws IOException {
		String key = "aaxmlrequest=true";
		int pos = redirect.indexOf(key);
		if (pos != -1)
			redirect = redirect.substring(0, pos) + redirect.substring(pos + key.length());

		this.redirect = redirect;
	}

	protected void processBuffer() {
		long tstart = System.currentTimeMillis();
		String content = "";
		if (this.getWriterBuffer() != null) {// servlet 只能 getWriter或者getOutputStream使用其中一个
			content = this.getWriterBuffer().toString();
		} else if (this.getStreamBuffer() != null) {
			String encoding = this.getOriginalResponse().getCharacterEncoding();
			try {
				content = this.getStreamBuffer().toString(encoding);
			} catch (UnsupportedEncodingException e) {
				content = this.getStreamBuffer().toString();
			}
		}

		zoneHtmlMap = new HashMap<String, String>();
		Matcher m = startZonePattern.matcher(content);
		while (m.find()) {
			int cur = m.start();

			String zoneStartDelimiter = m.group();
			cur += zoneStartDelimiter.length();
			String zone = zoneStartDelimiter.substring(
					zoneStartDelimiter.indexOf(ZONE_HTML_ID_PREFIX) + ZONE_HTML_ID_PREFIX.length(),
					zoneStartDelimiter.lastIndexOf("\""));

			int start = cur;// substring start

			StringMatcher endOfZone = new StringMatcher(END_OF_ZONE_PREFIX + zone);// 必须加上zone name 防止zone 嵌套
			for (; cur < content.length(); cur++) {
				endOfZone.push(content.charAt(cur));
				if (endOfZone.isTarget()) {
					cur++;// substring end is exclusive
					break;
				}
			}

			// 回溯，去掉整个的prefix <!-- @end of zone [zoneName]@ --></div>
			cur -= endOfZone.length();
			for (; cur > start; cur--) {
				if (content.charAt(cur) == '<') {
					// cur --;//no --, substring is end index exclusive
					break;
				}
			}

			zoneHtmlMap.put(zone, content.substring(start, cur));
		}
		long tend = System.currentTimeMillis();
		LOGGER.debug("Time costs for processing buffer: " + (tend - tstart));
	}

	public String findZoneContent(String zone) {
		// 优化，在处理完后直接进行hash
		return zoneHtmlMap.get(zone);
	}

	public void setContentType(String string) {
		// do nothing
	}

	public void flushBuffer() throws IOException {
		// do nothing
	}

	public void setCharacterEncoding(String string) {
		// do nothing
	}

	public void setDateHeader(String string, long l) {
		// do nothing
	}

	public void addDateHeader(String string, long l) {
		// do nothing
	}

	public void setHeader(String string, String string1) {
		// do nothing
	}

	public void addHeader(String string, String string1) {
		// do nothing
	}

	public void setIntHeader(String string, int i) {
		// do nothing
	}

	public void addIntHeader(String string, int i) {
		// do nothing
	}

	public StringWriter getWriterBuffer() {
		return writerBuffer;
	}

	public ByteArrayOutputStream getStreamBuffer() {
		return streamBuffer;
	}
}
